package tech.yixiyun.framework.kuafu.component.register;


import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;
import tech.yixiyun.framework.kuafu.bean.BeanContext;
import tech.yixiyun.framework.kuafu.bean.BeanDefinition;
import tech.yixiyun.framework.kuafu.context.ApplicationContext;
import tech.yixiyun.framework.kuafu.enhance.ClassInterceptor;
import tech.yixiyun.framework.kuafu.enhance.EnhanceKit;
import tech.yixiyun.framework.kuafu.log.LOGGER;

/**
 * 需要AOP的类解析，为其生成代理类
 */
public class EnhanceComponentRegistrar implements IComponentRegistrar {
    @Override
    public void register(Class clazz) {
        Class[] enhanceClasses = EnhanceKit.getEnhanceClasses(clazz);
        if (enhanceClasses == null || enhanceClasses.length == 0) {
            return;
        }

        BeanDefinition definition = BeanContext.getBeanDefinition(clazz);
        if (definition == null) {
            definition = BeanContext.register(clazz, new BeanDefinition(clazz));
        }
        definition.setEnhancerClasses(enhanceClasses);

        //TODO 如果后期发现动态生成代理类太费时间，可以考虑在每个definition中创建一个lock
        //生成前加锁,然后将finalClass设成null，然后由线程池负责生成代理类，生成后再释放锁，更新finalClass
        //如果生成期间，有其他方法要调用getFinalClass，就检测finalClass是不是null，不是直接返回，是的话，等待锁
        definition.setFinalClass(generateProxyClass(clazz));
        LOGGER.trace("代理类生成：" + clazz.getName());
    }

    /**
     * 生成代理类
     * @param clazz
     * @return
     */
    private <T> Class generateProxyClass(Class<T> clazz) {
        DynamicType.Unloaded<T> make = new ByteBuddy().subclass(clazz)
                .name("dynamic." + clazz.getName())
                .method(ElementMatchers.any())
                .intercept(MethodDelegation.to(ClassInterceptor.INSTANCE))
                //下面这种方式生成的代理类性能差，观察生成的字节码发现 每次都getMethod，没有缓存
//                .intercept(Advice.to(LoggerInterceptor.class))
                .make();
        DynamicType.Loaded<T> loaded = make.load(ApplicationContext.getServletContext().getClassLoader());
        return loaded.getLoaded();
    }



}
